/**
 * @author jeib
 */


//observacion (solo para entendidos).. X+ hacia der, Y+ para arriba, Z+ para atras!!! (U,V,N)


	
	
	FlyControls = function ( object, domElement, controlesHabilitados ) {

		var _this = this;
		this.object = object;
		this.object.matrixAutoUpdate = false;//JEIB
		
		this.controlsEnabled = (controlesHabilitados == undefined)? true : controlesHabilitados;//JEIB
		this.initialQuat = new THREE.Quaternion(); 
	
		
		this.domElement = ( domElement !== undefined ) ? domElement : document;
		if ( domElement ) this.domElement.setAttribute( 'tabindex', -1 );
		
		// API
		this.movementSpeed = 1.0;
		this.rollSpeed = 0.005;

		this.dragToLook = false;
		this.autoForward = false;

		// disable default target object behavior
		this.object.useQuaternion = true;

		// internals
		this.tmpQuaternion = new THREE.Quaternion();
		
		this.mouseStatus = 0;
		this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0,
							pitchUp: 0, pitchDown: 0, 
							yawLeft: 0, yawRight: 0,
							rollLeft: 0, rollRight: 0,
							rotateYLeft : 0, rotateYRight : 0,
							//rotateXUp : 0, rotateXDown : 0
							};
		
		//Teclas asignadas a cada estado
		this.TECLADO = {
				 SHIFT: 16,/* shift */
				 UP: 82,/*R*/ DOWN: 70,/*F*/ 
				 LEFT: 37,/*left*/ RIGHT: 39,/*right*/
				 FORWARD: 38,/*up*/BACK: 40,/*down*/
				 PITCH_UP: 87,/*W*/  PITCH_DOWN: 83,/*S*/
				 YAW_LEFT: 74,/*J*/ YAW_RIGHT: 76,/*L*/
				 ROLL_LEFT: 81,/*Q*/ ROLL_RIGHT: 69,/*E*/
				 ROTATE_Y_LEFT : 65,/*A*/ ROTATE_Y_RIGHT: 68, /*D*/
				 RESTAURAR_CAMARA : 80, /*P*/ 
		};
		
		this.moveVector = new THREE.Vector3( 0, 0, 0 );
		this.rotationVector = new THREE.Vector3( 0, 0, 0 );

		this.lastUpdate = -1;
		this.tdiff = 0;

		this.roll = 0;
		
		//angulos acumulados
		this.anguloPitch = 0;
		this.anguloRoll = 0;
		this.anguloYaw = 0;
		this.anguloEjeY = 0; //rotacion horizontal
		
		this.estadoRestaurar = {};
		
		//setea las condiciones iniciales para ser luego restauradas
		//params (opcional)
		//<anguloPitch> : angulo de rotacion respecto al eje U
		//<anguloYaw> : angulo de rotacion respecto al eje V
		//<anguloRoll> : angulo de rotacion respecto al eje N
		//<rotY> : angulo de rotacion respecto al eje Y
		//<position> : posicion de la camara
		this.settearCondInic = function(params){
			if (!params) params = {};
			this.estadoRestaurar.anguloEjeY = params.rotY || this.anguloEjeY;
			this.estadoRestaurar.anguloPitch = params.anguloPitch || this.anguloPitch;
			this.estadoRestaurar.anguloYaw = params.anguloYaw || this.anguloYaw;
			this.estadoRestaurar.anguloRoll = params.anguloRoll || this.anguloRoll;
			this.estadoRestaurar.position = new THREE.Vector3();
			this.estadoRestaurar.position.copy(this.object.position);
			this.initialQuat.copy(this.object.quaternion);
		};
		
		//restaura las condiciones iniciales de la camara
		//si se le pasa el parametro con las opciones, primero se setean las condiciones iniciales
		//params (opcional)
		//<anguloPitch> : angulo de rotacion respecto al eje U
		//<anguloYaw> : angulo de rotacion respecto al eje V
		//<anguloRoll> : angulo de rotacion respecto al eje N
		//<rotY> : angulo de rotacion respecto al eje Y
		//<position> : posicion de la camara
		this.restaurarCondInic = function(params){
			if (params) this.settearCondInic(params);
			
			this.object.matrix.identity();
			this.object.quaternion.copy(this.initialQuat);
			
			this.object.matrix.setRotationFromQuaternion( this.object.quaternion );
			this.object.matrixWorldNeedsUpdate = true;
			//this.object.update();
			this.object.position.copy(this.estadoRestaurar.position);
					
			this.anguloRoll = this.estadoRestaurar.anguloYaw;
			this.anguloPitch = this.estadoRestaurar.anguloPitch;
			this.anguloRoll = this.estadoRestaurar.anguloRoll;
			this.anguloEjeY = this.estadoRestaurar.anguloEjeY;
		};
		
		
		this.handleEvent = function ( event ) {
			if ( typeof this[ event.type ] == 'function' ) {
				this[ event.type ]( event );
			}
		};

		
		//++++++++++++++++++++++++++++++++++++
		//EVENTO KEYDOWN
		//++++++++++++++++++++++++++++++++++++
		this.keydown = function( event ) {

			if (!this.controlsEnabled || DIALOG_MANAGER.algunDialogoAbierto()) return;//TODO JEIB
			
			if ( event.altKey ) {return;}

			var teclaNoUsada = true;
			for (var i in this.TECLADO){if (this.TECLADO[i] == event.keyCode ) {teclaNoUsada = false; break; }}
			if (teclaNoUsada) return;
			
			event.preventDefault();//jeib
			event.stopPropagation();//jeib
			
			
			
			switch( event.keyCode ) {

					case this.TECLADO.SHIFT		: 	this.movementSpeedMultiplier = .1; break;
	
					case this.TECLADO.FORWARD	:	this.moveState.forward = 1; break;
					case this.TECLADO.BACK		:	this.moveState.back = 1; break;
	
					case this.TECLADO.LEFT		:  	this.moveState.left = 1; break;
					case this.TECLADO.RIGHT		:  	this.moveState.right = 1; break;
	
					case this.TECLADO.UP		:  	this.moveState.up = 1; break;
					case this.TECLADO.DOWN		:  	this.moveState.down = 1; break;
	
					case this.TECLADO.PITCH_UP	:	this.moveState.pitchUp = 1; break;
					case this.TECLADO.PITCH_DOWN:	this.moveState.pitchDown = 1; break;
	
//					case this.TECLADO.YAW_LEFT	:	this.moveState.yawLeft = 1; break;
//					case this.TECLADO.YAW_RIGHT	:   this.moveState.yawRight = 1; break;
	
					//case this.TECLADO.ROLL_LEFT	:   this.moveState.rollLeft = 1; break;
					//case this.TECLADO.ROLL_RIGHT:   this.moveState.rollRight = 1; break;
					
					case this.TECLADO.ROTATE_Y_LEFT 			:   this.moveState.rotateYLeft = 1; break;
					case this.TECLADO.ROTATE_Y_RIGHT 		:   this.moveState.rotateYRight = 1; break;
					
					case this.TECLADO.RESTAURAR_CAMARA 		:   this.restaurarCondInic(); break;
			}

			this.updateMovementVector();
			this.updateRotationVector();

		};

		
		//++++++++++++++++++++++++++++++++++++
		//EVENTO KEYUP
		//++++++++++++++++++++++++++++++++++++
		this.keyup = function( event ) {

			if (!this.controlsEnabled || DIALOG_MANAGER.algunDialogoAbierto()) return;//TODO JEIB

			var teclaNoUsada = true;
			for (var i in this.TECLADO){if (this.TECLADO[i] == event.keyCode ) {teclaNoUsada = false; break; }}
			if (teclaNoUsada) return;
			
			event.preventDefault();//jeib
			event.stopPropagation();//jeib
			
			switch( event.keyCode ) {

				case this.TECLADO.SHIFT		: 	this.movementSpeedMultiplier = 0; break;
	
				case this.TECLADO.FORWARD	:	this.moveState.forward = 0; break;
				case this.TECLADO.BACK		:	this.moveState.back = 0; break;
	
				case this.TECLADO.LEFT		:  	this.moveState.left = 0; break;
				case this.TECLADO.RIGHT		:  	this.moveState.right = 0; break;
	
				case this.TECLADO.UP		:  	this.moveState.up = 0; break;
				case this.TECLADO.DOWN		:  	this.moveState.down = 0; break;
	
				case this.TECLADO.PITCH_UP	:	this.moveState.pitchUp = 0; break;
				case this.TECLADO.PITCH_DOWN:	this.moveState.pitchDown = 0; break;
	
//				case this.TECLADO.YAW_LEFT	:	this.moveState.yawLeft = 0; break;
//				case this.TECLADO.YAW_RIGHT	:   this.moveState.yawRight = 0; break;
	
				//case this.TECLADO.ROLL_LEFT	:   this.moveState.rollLeft = 0; break;
				//case this.TECLADO.ROLL_RIGHT:   this.moveState.rollRight = 0; break;
				
				case this.TECLADO.ROTATE_Y_LEFT 			:   this.moveState.rotateYLeft = 0; break;
				case this.TECLADO.ROTATE_Y_RIGHT 		:   this.moveState.rotateYRight = 0; break;
			}

			this.updateMovementVector();
			this.updateRotationVector();

		};

	
		this.getUpVector = function(){
			////console.log("matriz", this.object.matrix);
			return new THREE.Vector3(this.object.matrix.n21, 
					this.object.matrix.n22,
					-this.object.matrix.n23).normalize();
		};
		
		//falta debug
		this.getLookAtVector = function(){
			////console.log("matriz", this.object.matrix);
			return new THREE.Vector3(this.object.matrix.n31, 
					this.object.matrix.n32,
					-this.object.matrix.n33).normalize();
		};
		
		
		//genera valores de pitch entre [-PI/2; PI/2]
		//genera valores de rotY entre [-PI; PI]
		//si simular = true, no aplica los cambios, solo devuelve los angulos de pitch y rotY
		this.lookAtTarget = function(target, simular){
			//TODO sacar los 'acos'(el dot tira directamente los valores entre -pi y pi) y calcular rotY con respecto a (0,0,-1) en vez de (1,0,0)
	
			var look = null;
			
			var up = new THREE.Vector3(0,1,0);
			
			if (this.object.position.x == target.x && this.object.position.z == target.z ){ //si estoy parado encima o debajo del target
				//console.log("*--*-*-*-*- 1111111111111111111");
				if (this.object.position.y >= target.y){
					look = new THREE.Vector3(0,0,-1); 
				}else{
					look = new THREE.Vector3(0,0,1);
				}
				
				if (simular){
					return {rotY : 0, pitch: -Math.PI/2};
				}
				this.rotarHorizontalmenteAbsoluto(0);
				this.rotarPitchAbsoluto(-Math.PI/2);
				return;
			}else{
				look = new THREE.Vector3(target.x - this.object.position.x, 
						target.y - this.object.position.y,
						target.z - this.object.position.z).normalize();
			}
			
		
			
			
			matrix =  new THREE.Matrix4();
			
//			matrix.lookAt(this.object.position, target, new THREE.Vector3(0,0,-1));
			matrix.lookAt(this.object.position, target, up);
			
			var quaternion_lookAt  = matrix.decompose(matrix)[1];//me devuelve el quaternion
//			//console.log("quaternion_lookAt",quaternion_lookAt);
//			//console.log("quaternion_lookAt x: ",quaternion_lookAt.x,"y: ",quaternion_lookAt.y,"z: ",quaternion_lookAt.z,	"w: ",quaternion_lookAt.w);
			
			
			var pitchAng = 0;
			var rotYAng = 0;
			
			var vectPitch = look.clone();
			vectPitch.y = 0;
			//vectPitch.normalize();
//			//console.log("vectPitch",vectPitch);
			
			//if (vectPitch.y || vectPitch.z)
			{
				
				pitchAng = Math.acos(vectPitch.dot(look));
				//console.log("pitchAng --- antes", pitchAng);	
				if (look.y < 0){ pitchAng = -pitchAng;}
			}
		
			
			var ejeMinZ = new THREE.Vector3(0,0,-1);
			var vectRotY = look.clone();
			vectRotY.y = 0;
			vectRotY.normalize();
			//vectRotY.normalize();
			//console.log("vectRotY",vectRotY);
			
			if (vectRotY.x || vectRotY.z){
				// 
				 var rotYAng = Math.acos(vectRotY.dot(new THREE.Vector3(0,0,-1)));
				
				
				if (vectRotY.x >=0){
					if (vectRotY.z >= 0){rotYAng = -rotYAng;}
					else{rotYAng =  -rotYAng;	}
				}
			}
			
			if (!simular){
				this.anguloRoll = 0;
				this.anguloYaw = 0;
				
				this.rotarHorizontalmenteAbsoluto(rotYAng);
				this.rotarPitchAbsoluto(pitchAng);
			}
			
			return {rotY : rotYAng, pitch: pitchAng};
		};
		
		//desplazo sobre el plano XZ, de acuerdo a la orientaci�n que tenga la camara
		//(calculado a partir del vector n)
		this.desplazarXZ = function(vector, escala){
			vector.multiplyScalar(escala);
			this.object.translateX(vector.x);
			this.object.translate(vector.z,new THREE.Vector3(0,Math.sin(this.anguloPitch),Math.cos(this.anguloPitch)).normalize());
		};
				
			//rota la camara alrededor del eje Y en un angulo 'angulo' (en radianes) a partir de la orientacion actual
			this.rotarHorizontalmente = function(angulo){
				if (!angulo) return; //no hago nada
				this.anguloEjeY += angulo;
				
				matrix =  new THREE.Matrix4();
				matrix.makeRotationY(angulo);
				var cuaternionRotEjeY  = matrix.decompose(matrix)[1];//me devuelve el quaternion
				cuaternionViejo = this.object.quaternion; //cuaternion actual de la camara
				
				this.object.quaternion = cuaternionRotEjeY;
				this.object.quaternion.multiplySelf( cuaternionViejo);
				this.object.matrixWorldNeedsUpdate = true;
				//this.object.update();
			};
			
			//rota la camara alrededor del eje U (pitch) en un angulo 'angulo' (en radianes) a partir de la orientacion actual
			this.rotarPitch = function(angulo){
				if (!angulo) return; //no hago nada
				
				this.anguloPitch += angulo;
				this.tmpQuaternion.set( Math.sin(angulo/2), 0, 0, Math.cos(angulo/2) ).normalize();
				this.object.quaternion.multiplySelf( this.tmpQuaternion );
				this.object.matrixWorldNeedsUpdate = true;
				//this.object.update();
			};
			
			//rota la camara alrededor del eje V (yaw) en un angulo 'angulo' (en radianes) a partir de la orientacion actual
			this.rotarYaw = function(angulo){
				if (!angulo) return; //no hago nada
				
				this.anguloYaw += angulo;
				this.tmpQuaternion.set( 0, Math.sin(angulo/2), 0, Math.cos(angulo/2) ).normalize();
				this.object.quaternion.multiplySelf( this.tmpQuaternion );
				this.object.matrixWorldNeedsUpdate = true;
				//this.object.update();
			};
			

			//rota la camara alrededor del eje N (roll) en un angulo 'angulo' (en radianes) a partir de la orientacion actual
			this.rotarRoll = function(angulo){
				if (!angulo) return; //no hago nada
				
				this.anguloRoll += angulo;
				this.tmpQuaternion.set(0, 0,  Math.sin(angulo/2), Math.cos(angulo/2) ).normalize();
				this.object.quaternion.multiplySelf( this.tmpQuaternion );
				this.object.matrixWorldNeedsUpdate = true;
				//this.object.update();
			};
			
			
			
			//orienta la camara luego de rotar 'angulos' radianes a partir del origen (comienzo del angulo)
			this.rotarHorizontalmenteAbsoluto = function(anguloAbsoluto){
				this.rotarHorizontalmente(-this.anguloEjeY + anguloAbsoluto);
			};
			
			
			//rota la camara alrededor del eje U (pitch) en un angulo 'anguloAbsoluto' (en radianes) de forma absoluta
			this.rotarPitchAbsoluto = function(anguloAbsoluto){
				this.rotarPitch(-this.anguloPitch + anguloAbsoluto);
			};
			
			//rota la camara alrededor del eje V (pitch) en un angulo 'anguloAbsoluto' (en radianes) de forma absoluta
			this.rotarYawAbsoluto = function(anguloAbsoluto){
				this.rotarYaw(-this.anguloYaw + anguloAbsoluto);
			};
			
			//rota la camara alrededor del eje N (roll) en un angulo 'anguloAbsoluto' (en radianes) de forma absoluta
			this.rotarRollAbsoluto = function(anguloAbsoluto){
				this.rotarRoll(-this.anguloRoll + anguloAbsoluto);
			};
			
			
			
		this.update = function( parentMatrixWorld, forceUpdate, camera ) {
////			this.object.lookAt(new THREE.Vector3(200,0,0));
////			this.object.matrixWorldNeedsUpdate = true;
////			this.object.updateMatrix();

			var now = new Date().getTime();

			if ( this.lastUpdate == -1 ) this.lastUpdate = now;

			this.tdiff = ( now - this.lastUpdate ) / 1000;
			this.lastUpdate = now;

			var moveMult = this.tdiff * this.movementSpeed;
			var rotMult = this.tdiff * this.rollSpeed;

//			this.object.translateX( this.moveVector.x * moveMult );
			this.object.translateY( this.moveVector.y * moveMult );
//			this.object.translateZ( this.moveVector.z * moveMult );
			
//			this.object.translateX( this.moveVector.x * moveMult );
//			this.object.translateY( this.moveVector.y * moveMult );
//			this.object.translateZ( this.moveVector.z * moveMult );
		var vectorXZ = new THREE.Vector3( this.moveVector.x, 0 , this.moveVector.z);
		if (this.moveVector.x || this.moveVector.z){ 
			var val = Math.abs(this.object.position.y)/75 || 5;
			_this.desplazarXZ(vectorXZ, val);
		}
			
			if (this.rotationVector.x){	this.anguloPitch += rotMult*this.rotationVector.x;}//JEIB
			if (this.rotationVector.y){	this.anguloYaw += rotMult*this.rotationVector.y;}//JEIB
			if (this.rotationVector.z){	this.anguloRoll += rotMult*this.rotationVector.z;}//JEIB
			
			
			this.tmpQuaternion.set( this.rotationVector.x * Math.sin(rotMult/2), this.rotationVector.y * Math.sin(rotMult/2), this.rotationVector.z* Math.sin(rotMult/2), Math.cos(rotMult/2) ).normalize();
			this.object.quaternion.multiplySelf( this.tmpQuaternion );
		
			this.object.matrix.setPosition( this.object.position );
			this.object.matrix.setRotationFromQuaternion( this.object.quaternion );
			this.object.matrixWorldNeedsUpdate = true;
			//this.object.update();
			
			//entro si no estan simultaneamente apretados
			if (this.moveState.rotateYRight - this.moveState.rotateYLeft){ 
				this.rotarHorizontalmente((this.moveState.rotateYLeft)? rotMult : -rotMult);
			}
			
			//log angulos en degrees
			if (this.roll){//TODO borrar (esto esta para chequeos)
				var x = this.anguloPitch*180/(Math.PI);
				var y = this.anguloEjeY*180/(Math.PI);
				//console.log("quaternion", this.object.quaternion);
				//console.log("this.anguloPitch",x,"this.anguloEjeY",y);
				this.roll = 0;
			}
		};
		
	
		
		this.updateMovementVector = function() {
			var forward = ( this.moveState.forward || ( this.autoForward && !this.moveState.back ) ) ? 1 : 0;

			this.moveVector.x = ( -this.moveState.left    + this.moveState.right );
			this.moveVector.y = ( -this.moveState.down    + this.moveState.up );
			this.moveVector.z = ( -forward + this.moveState.back );

//			//console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
		};

		this.updateRotationVector = function() {

			this.rotationVector.x = ( -this.moveState.pitchDown + this.moveState.pitchUp );
			this.rotationVector.y = ( -this.moveState.yawRight  + this.moveState.yawLeft );
			this.rotationVector.z = ( -this.moveState.rollRight + this.moveState.rollLeft );

//			//console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
		};

		this.getContainerDimensions = function() {

			if ( this.domElement != document ) {

				return {
					size	: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
					offset	: [ this.domElement.offsetLeft,  this.domElement.offsetTop ]
				};

			} else {

				return {
					size	: [ window.innerWidth, window.innerHeight ],
					offset	: [ 0, 0 ]
				};

			}

		};

		function bind( scope, fn ) {

			return function () {

				fn.apply( scope, arguments );

			};

		};

//		this.domElement.addEventListener( 'mousemove', bind( this, this.mousemove ), false );
//		this.domElement.addEventListener( 'mousedown', bind( this, this.mousedown ), false );
//		this.domElement.addEventListener( 'mouseup',   bind( this, this.mouseup ), false );

		this.domElement.addEventListener( 'keydown', bind( this, this.keydown ), false );
		this.domElement.addEventListener( 'keyup',   bind( this, this.keyup ), false );
		//TODO definir el onBlur 
		
		
		this.updateMovementVector();
		this.updateRotationVector();
		
		//console.log("this.object.matrix - primera matriz",this.object.matrix);
	
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		
		//TODO: deprecated. por ahora no sirven (eventos del mouse)
		this.mousedown = function( event ) {

			if ( this.domElement !== document ) {

				this.domElement.focus();

			}

			event.preventDefault();
			event.stopPropagation();

			if ( this.dragToLook ) {

				this.mouseStatus ++;

			} else {

				switch ( event.button ) {

					case 0: this.object.moveForward = true; break;
					case 2: this.object.moveBackward = true; break;

				}

			}

		};

		this.mousemove = function( event ) {

			if ( !this.dragToLook || this.mouseStatus > 0 ) {

				var container = this.getContainerDimensions();
				var halfWidth  = container.size[ 0 ] / 2;
				var halfHeight = container.size[ 1 ] / 2;

				this.moveState.yawLeft   = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth  ) / halfWidth;
				this.moveState.pitchDown =   ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;

				this.updateRotationVector();

			}

		};

		this.mouseup = function( event ) {

			event.preventDefault();
			event.stopPropagation();

			if ( this.dragToLook ) {

				this.mouseStatus --;

				this.moveState.yawLeft = this.moveState.pitchDown = 0;

			} else {

				switch ( event.button ) {

					case 0: this.moveForward = false; break;
					case 2: this.moveBackward = false; break;

				}

			}

			this.updateRotationVector();

		};
		
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		

		//<<ANIMACION DE LA CAMARA>>
		//<<Estas funciones animan la posicion y orientacion de la camara>>
		//<<Retornan TWEENs (ver libreria)>>
		
		
		/*anima la orientacion de la camara, rotando sobre el eje Y y sobre el eje U (pitch), hasta quedar
		 * alineada con la posicionTarget*/
		this.animarOrientacionCamara = function(posicionTarget, duracion, callback ){
				
			//obs: el segundo parametro es para "simular" el lookAt (no actualiza los valores de la camara)
			simTargetParams = this.lookAtTarget(posicionTarget, true);//return {rotY : rotYAng, pitch: pitchAng}
			
			tweenOrientacion = new TWEEN.Tween( {
												pitchAng: _this.anguloPitch, 
												rotYAng : _this.anguloEjeY
												} )
			.to( {	pitchAng: simTargetParams.pitch,  
					rotYAng : simTargetParams.rotY },
					duracion)
			.onUpdate(function(){
				 _this.rotarPitchAbsoluto(this.pitchAng); //TODO (this no es flycontrols)
				 _this.rotarHorizontalmenteAbsoluto(this.rotYAng );	
			})	
			.onComplete(function(){
				_this.lookAtTarget(posicionTarget);
				if (callback) callback();
			});
			
			return tweenOrientacion;
		};


		// coordenadas: X = X, Y = Y, Z = Z
		//reposiciona la camara dada la posicion inicial, y la posicion del objeto al que se desea observar (LOOK AT) 
		//al terminar la animacion
		this.posicionarCamaraTarget = function(posicionCamara, posicionTarget, duracion, callback){
			
			//obs: el segundo parametro es para "simular" el lookAt (no actualiza los valores de la camara)
			//simTargetParams = _controlCamaraFly.lookAtTarget(posicionTarget, true);//return {rotY : rotYAng, pitch: pitchAng}
			
				
			tweenCamaraPosicion = new TWEEN.Tween( _this.object.position )
			.to( {	x: posicionCamara.x ,// - distanciaACamara,
				y: posicionCamara.y ,
				z: posicionCamara.z  }, // + 300},
				duracion)
				.onUpdate(function(){
							_this.lookAtTarget({x : posicionTarget.x, y: posicionTarget.y, z: posicionTarget.z});
				})
				.easing( TWEEN.Easing.Linear.None )
				.onComplete( function(){
					_this.lookAtTarget({x : posicionTarget.x, y: posicionTarget.y, z: posicionTarget.z});
					if(callback) callback();		
				} );
			
			return tweenCamaraPosicion;
			//tweenCamaraPosicion.start();
			
		};


		//rota la camara rotY y pitch angulos en un tiempo 'duracion'
		//<posicion> : posicion del punto del camino a animar
		//<callback> : funcion a llamar cuando se finalice el tweening
		this.rotarCamara = function(rotY, pitch, duracion, callback){
			
			
			var a = Math.abs(_this.anguloEjeY) + Math.abs(rotY);
//				
			if (a > Math.PI){
				//alert("a-->"+a);	
				//console.log("ANTESSSSSSSSS _controlCamaraFly.anguloEjeY ",_controlCamaraFly.anguloEjeY , "rotY",rotY);
				if (_this.anguloEjeY < 0 && rotY>0 ){
					_this.anguloEjeY += Math.PI*2;
					//console.log("1");
				}else if (_this.anguloEjeY > 0  && rotY<0){
					_this.anguloEjeY -= Math.PI*2;
					//console.log("2");
				}
				//console.log("DESPUESSSSSSSSSS _controlCamaraFly.anguloEjeY ",_controlCamaraFly.anguloEjeY , "rotY",rotY);	
			}	
			var tweenPosicionarCamara =
				new TWEEN.Tween({			
					angPitch: _this.anguloPitch,
					angY: _this.anguloEjeY,
				})
			.to( {
				
				angPitch: pitch,
				angY: rotY 
			}, // + 300},
			duracion)
			.onUpdate(function(){
				_this.rotarPitchAbsoluto(this.angPitch);
				_this.rotarHorizontalmenteAbsoluto(this.angY);			
			})
			.easing( TWEEN.Easing.Linear.None )
			.onComplete( function(){if(callback) callback();} );
			
			return tweenPosicionarCamara;			
			
//				alert('falta posicionar a la camara al principio de la animacion');
		};





			//<posicion> : posicion del punto del camino a animar
			//<callback> : funcion a llamar cuando se finalice el tweening
		//<climbY> : cuando "sube" la camara en Y (especie de parabola)
			this.posicionarCamara = function(posicion, rotY, pitch, duracion, callback, climbY){
					
				climbY = (climbY == undefined)? 0 : climbY;
				var a = Math.abs(_this.anguloEjeY) + Math.abs(rotY);
//				
				if (a > Math.PI){
					//alert("a-->"+a);	
					//console.log("ANTESSSSSSSSS _controlCamaraFly.anguloEjeY ",_controlCamaraFly.anguloEjeY , "rotY",rotY);
					if (_this.anguloEjeY < 0 && rotY>0 ){
						_this.anguloEjeY += Math.PI*2;
						//console.log("1");
					}else if (_this.anguloEjeY > 0  && rotY<0){
						_this.anguloEjeY -= Math.PI*2;
						//console.log("2");
					}
					//console.log("DESPUESSSSSSSSSS _controlCamaraFly.anguloEjeY ",_controlCamaraFly.anguloEjeY , "rotY",rotY);	
				}
				
				if (!duracion){//no queremos una animacion, sino que asigne los valores
					_this.object.position.x = posicion.x; //_this.object = camera
					_this.object.position.y = posicion.y;
					_this.object.position.z = posicion.z;
			 		_this.rotarPitchAbsoluto(pitch);
					_this.rotarHorizontalmenteAbsoluto(rotY);	
					return;
				}
					
				var tweenPosicionarCamara =
						new TWEEN.Tween({
											 x: _this.object.position.x,
										//	 y: _this.object.position.y,
											 z: _this.object.position.z,
											 angPitch: _this.anguloPitch,
											 angY: _this.anguloEjeY,
										})
						.to( {
								x: posicion.x ,// - distanciaACamara,
							//	y: posicion.y,
								z: posicion.z,
								angPitch: pitch,
								angY: rotY 
							}, // + 300},
							duracion)
						.onUpdate(function(){
									_this.object.position.x = this.x;
								
								//	_this.object.position.y = this.y;
									_this.object.position.z = this.z;
							 		_this.rotarPitchAbsoluto(this.angPitch);
									_this.rotarHorizontalmenteAbsoluto(this.angY);			
						
								})
						.easing( TWEEN.Easing.Linear.None )
						.onComplete( function(){if(callback) callback();} );
				
				
			var tweenY =new TWEEN.Tween({
					y: _this.object.position.y,
				})
				.to( {
					y: posicion.y + climbY,
				}, // + 300},
				duracion/2)
				.easing( TWEEN.Easing.Exponential.Out )
//				.easing( TWEEN.Easing.Quintic.EaseOut )
				.onUpdate(function(){
					_this.object.position.y = this.y;
					
				});
			
			var tweenY2 =new TWEEN.Tween({
				y: posicion.y + climbY,
			})
				.to( {
					y: posicion.y ,
				}, // + 300},
				duracion/2)
				.easing( TWEEN.Easing.Exponential.In )
//				.easing( TWEEN.Easing.Quintic.In )
				.onUpdate(function(){
					//console.log('entre', this.y)
					_this.object.position.y = this.y;
					
				})
				.easing( TWEEN.Easing.Linear.None );
			tweenY.chain(tweenY2);
			
			var tweenDummy = new TWEEN.Tween({x:0}).to({x:1},1).onComplete(function(){
			if (callback) 
				//callback();
				tweenPosicionarCamara.start();
				tweenY.start();
			});
//				return tweenPosicionarCamara;			
					return tweenDummy;
//				alert('falta posicionar a la camara al principio de la animacion');
			};
		//<<fin ANIMACION DE LA CAMARA>>

		
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			

	};



